class Stardust {
  static minWidth = 100
  static minHeight = 100
  static maxRadius = 20

  constructor(elContainer, config = {}) {
    this.$canvas = document.createElement('canvas')
    this.$container = elContainer
    this.$container.appendChild(this.$canvas)
    this.context = this.$canvas.getContext('2d')
    this.dustCount = (config && config.count) || 50
    this.dust = []
    this.width = 0
    this.height = 0
    this.size = (config && config.size) || 200
    this.hue = 0

    this.init()
    this.render()
  }

  init() {
    this.resize()
    window.addEventListener('resize', this.resize.bind(this))
    this.dust = Array(this.dustCount)
      .fill(0)
      .map((v, i) => this.createDust(i, this.width, this.height, this.size))
  }

  resize() {
    const width = Math.max(this.$container.offsetWidth, Stardust.minWidth)
    const height = Math.max(this.$container.offsetHeight, Stardust.minHeight)
    this.width = width
    this.height = height
    this.$canvas.width = width
    this.$canvas.height = height
  }

  createDust(index = 0) {
    const radius = getRandom(2, Stardust.maxRadius)
    const y = getRandom(0.5 * (this.height - this.size), 0.5 * (this.height + this.size))
    const alpha = getRandom(0.1, 1)
    return {
      index,
      x: -radius - getRandom(0, this.width),
      y,
      startY: y,
      amplitude: 0.25 * this.size + getRandom(50, 100),
      radius,
      alpha,
      color: {
        r: getRandom(100, 255),
        g: getRandom(100, 255),
        b: getRandom(100, 255)
      },
      speed: 1 + getRandom(0, 1)
    }
  }

  drawDust() {
    this.dust.forEach(dust => {
      this.context.beginPath()
      this.context.fillStyle = `rgba(${dust.color.r},${dust.color.g},${dust.color.b},${dust.alpha})`
      this.context.arc(dust.x, dust.y, dust.radius, 0, 2 * Math.PI)
      this.context.fill()
    })
  }

  moveDust() {
    this.dust.forEach((dust, index) => {
      if (dust.x - dust.radius > this.width) {
        this.dust.splice(index, 1, this.createDust(index, this.width, this.height, this.size))
      } else {
        dust.x += dust.speed
        dust.y = dust.startY + dust.amplitude * Math.sin(((dust.x / 5) * Math.PI) / 180)
      }
    })
  }

  drawBackground() {
    const saturation = 0.8
    const lightness = 0.4
    const speed = 0.1
    this.hue = (speed + this.hue) % 360
    const linearGradient = this.context.createLinearGradient(0, 0, this.width, this.height)
    const color1 = HSL2RGB(this.hue, saturation, lightness, true)
    const color2 = HSL2RGB((this.hue + 60) % 360, saturation, lightness, true)

    linearGradient.addColorStop(0, color1)
    linearGradient.addColorStop(1, color2)
    this.context.fillStyle = linearGradient
    this.context.fillRect(0, 0, this.width, this.height)
  }

  render() {
    this.context.clearRect(0, 0, this.width + Stardust.maxRadius, this.height + this.size)
    this.drawBackground()
    this.drawDust()
    this.moveDust()
    requestAnimationFrame(this.render.bind(this))
  }
}

function getRandom(min = 0, max = 100) {
  return 0.001 * Math.floor(1000 * (Math.random() * (max - min) + min))
}

/**
 *
 * @param {Number} H 色相 {0,360]
 * @param {Number} S 饱和度 {0,1}
 * @param {Number} L 亮度 {0,1}
 * @param {Boolean} stringMode 返回字符串
 */
function HSL2RGB(H = 0, S = 0, L = 0, stringMode = false) {
  const C = (1 - Math.abs(2 * L - 1)) * S
  const X = C * (1 - Math.abs(((H / 60) % 2) - 1))
  const m = L - C / 2
  const vRGB = []
  if (H >= 0 && H < 60) {
    vRGB.push(C, X, 0)
  } else if (H >= 60 && H < 120) {
    vRGB.push(X, C, 0)
  } else if (H >= 120 && H < 180) {
    vRGB.push(0, C, X)
  } else if (H >= 180 && H < 240) {
    vRGB.push(0, X, C)
  } else if (H >= 240 && H < 300) {
    vRGB.push(X, 0, C)
  } else if (H >= 300 && H < 360) {
    vRGB.push(C, 0, X)
  }
  const [vR, vG, vB] = vRGB
  const R = 255 * (vR + m)
  const G = 255 * (vG + m)
  const B = 255 * (vB + m)

  if (stringMode) {
    return `rgb(${R},${G},${B})`
  }

  return { R, G, B }
}

export default Stardust
